home *** CD-ROM | disk | FTP | other *** search
-
- The first challenge was figuring out how to get the Macintosh resources
- into a form that could be dissected from within UNIX. First, I tried
- copying files over from "Executor", a Linux based X11 Macintosh emulator.
- Unfortunately, Executor wraps the resource fork in a special header,
- rendering it difficult to decipher. The solution was to BinHex the files
- on a Macintosh, and then use the Linux utility 'mcvert' to extract the
- resource fork from the archive. I also used the ALPHA 'hfs' filesystem
- for Linux, but got a file that was different than the one 'mcvert' unwrapped,
- so I didn't trust it.
-
- The second problem was finding out the format of the Macintosh resource
- fork, so that the resources could be extracted. This was solved by going
- to the bookstore and looking it up in the "Inside Macintosh" series
- published by Addison and Wesley. This series has detailed specifications
- on the internals of the Macintosh operating system. This was pointed out
- by Andrew Welch, the author of Maelstrom.
-
- The next tricky part was after I had written code to look at the various
- parts of the resource fork of the Maelstrom game, the values seemed way
- too large to be correct. The offset for the Resource Map was 4 MegaBytes
- into the file, when the file was only .5 Megabyte large. This was caused
- by the fact that Linux is little-endian, while Macintoshes are big-endian.
- This was solved by running ntohl() and ntohs() on the offset/value fields
- of the resource fork.
-
- I wrote a utility to list and extract all resources from a Macintosh binary
- resource fork.
-
- I extracted the sprites from the Maelstrom resources, and attempted to view
- them in a VGA console, using custom-crafted bitmap viewing utilities.
- The sprites appeared, but appeared in psychedelic colors. Evidently,
- the colormap was wrong. I played with the colormap, setting it to the
- standard colormap, which didn't help. I used ResEdit on the Macintosh
- to look into the ColorMap resources of Maelstrom. ResEdit has a colormap
- editor that can display and set the colormap of a Macintosh application.
- I used it to find out the proper colormap for displaying Maelstrom sprites.
-
- Next was the sounds. I used the resource parsing code to extract the
- sound resources, and then used "Inside Macintosh: Sounds" to look at
- the format of the sounds used in Maelstrom. It turns out they are all
- either format 1 or format 2 sampled sound bites which can be directly
- played on /dev/dsp under Linux.
-
- Then, I worked with the colormap resources. I found the format for the
- colormap resources in "Inside Macintosh: Imaging with QuickDraw"
- I used this description to write a utility that converts a binary clut
- resource into a C header file describing the colormap.
-
- Next was the PICT resources: the title screen, credits, and info screens.
- I looked at the "Inside Macintosh: Imaging with QuickDraw" for the PICT
- resource format, and freaked when I saw it was a collection of opcodes
- for the QuickDraw routines. Then I found that the first part of the
- Maelstrom PICTs seemed to be a colormap.
-
- The solution to the problem of understanding PICT resources was going
- to ftp.sunet.se:/pub/mac/mirror-umich/graphics/graphicsutil
- and retrieving the programs "GraphicConverter" and "pictmanagementutils"
- I used these programs to extract the PICT resources and save them
- as raw PPM files. Then I wrote a simple program to display raw PPM
- files on the VGA console.
-
- I copied the raw PPM files from the macintosh disk to the Linux system
- by mounting it with the 'hfs' module loaded, and then doing a straight
- copy of the data portions of the files. The 'hfs' module for Linux
- was written by Paul H. Hargrove, hargrove@sccm.stanford.edu
-
- I then translated PPM files to XPM format with xv, performing image
- enhancement. I then wrote a utility to merge the colormaps of the
- pixmaps and perform a check to see if all the colors are in the
- standard colormap of Maelstrom. They are, so I can convert them all
- into raw pixel data -- a form more suitable for blitting to the screen.
- This form also takes less disk-storage than either the raw PPM data
- or the textual XPM data.
-
- When loading up Maelstrom, I thought it might be nice to create a color
- icon for the window manager to use. I created it by resizing and
- simplifying the main title image of Maelstrom with xv and then turning
- it into a standard colormap xpm icon. The program 'xboing', written by
- Justin C. Kibell, jck@citri.edu.au, creates its own color icon. I looked
- at the code for this, and used the technique with Maelstrom.
- The technique is basically to create a pixmap on the X server and then
- tell the window manager to use it for an icon. This must be done before
- the window is mapped on the screen, otherwise the window manager ignores
- its hint.
-
- Then, I tried allocating a private colormap for displaying Maelstrom
- graphics. Well, that works, and the code is still in place to support
- that, but the Maelstrom colormap turns the rest of the screen flourescent
- colors. I looked at the code for 'xv', written by John Bradley, and
- found a good algorithm for mapping the colors needed to the existing
- color palette. I have ideas for more advanced algorithms of color
- allocation, but the direct mapping method seems to work well enough for
- me. One problem with this approach is that once Maelstrom is started,
- it locks all the colors in the colormap, preventing applications from
- allocating new shared color cells.
-
- One of the primary factors affecting game playability is the speed of
- real-time animation. One of the best ways to achieve high-speed animation
- in the X11 environment is to use the MITSHM extension. The standard
- method of working with X shared memory is to create a small (300x200)
- shared image, manipulate the image and then "put" it on the server
- with XShmPutImage(). Reasonable speed can be achieved with this method,
- however this is not fast enough for the needs of Maelstrom. Maelstrom
- is played in a 640x480 window, and copying a 2.5 megabyte image into
- the screen contents would take way too long. I get around this problem
- by creating a shared Pixmap, and making it the background of the window.
- Background pixmaps are tiled, so the background Pixmap has to be exactly
- the same size as the window. Now, the background of the window can be
- directly manipulated, and refreshed with XClearArea(). This approach seems
- to be much faster than working with an XImage, and blitting it to the window.
- One side-advantage of this approach is that graphics can be drawn in
- the foreground of the window, without affecting the animated background.
- Another benefit of this approach is that a redraw on an expose event
- simply consists of an XClearWindow() call, without having to redraw
- the entire contents of the graphics window.
-
- Next was sound mixing. I used a simplified version of the sound mixing
- code in 'sfxserver', written by Terry Evans, tevans@cs.utah.edu, for
- sound mixing. Each loop of the mixer compiles multiple channels of
- the sound mixer into a single chunk of sampled data which is sent to
- the audio device. The original idea was to have a continuous loop,
- first checking for input, and then writing a chunk of sound (or silence)
- to the sound device. This resulted in slow response time, because the
- a sound event had to wait the entire cycle of compiling the sound channels
- until it could be acted upon. I modified the original concept to support
- asynchronous input. Now, the loop has been simplified to a simple continuous
- play of the sound channels, but at any time during the compilation of a
- sound chunk, new sound data can be placed into channels, or removed from
- the mixing channels. This allows nearly instantaneous mixing of sound effects,
- in response to sound events.
-
- I decided to run the sound server as a separate process from Maelstrom.
- The sound server has to continuously play sampled data (sound or silence)
- and respond instantly to sound events. Maelstrom has to continuously
- update animated graphics. I thought the best way to perform both of these
- functions simultaneously was to do them in separately running processes,
- communicating through a private UNIX domain socket.
-
- PROBLEM: After we allocate a full colormap, Maelstrom wants to allocate
- other colors. We have completely filled the colormap, yet... what can
- we do? We can completely take over the root window colormap...
- That's not polite, but what else can we do?
-
- PROBLEM: After we allocate a full colormap, Maelstrom wants to allocate
- other colors. We have completely filled the colormap, yet... what can
- we do? We can completely take over the root window colormap...
- That's not polite, but what else can we do?
- ... Try to allocate the closest color in the colormap and see what we
- come up with...
-
- The best thing might be to grab a copy of the current screen and see
- what colors are actually in use, and reserve all the rest. Hmmm??
-
- Mapping to the closest color already in the colormap seems like it
- works. It's not perfect, but it is polite, and prevents lots of
- gyrations trying to blend a private and public colormap.
-
- I tried to allocate all the colors, and then map the ones that were
- left. That didn't work. The colormap was left full, and the colors
- that needed to be mapped didn't have any colors to map to.
-
- I wrote a routine to convert the Macintosh color icon resource into
- both XPM and Linux-Maelstrom sprite format.
-
- The next challenge is the fonts...
-
- I found a program called 'mac2bdf' that converts Macintosh FONT resources
- into UNIX bdf fonts. I'm using the information here to learn about the
- Macintosh font format. My plan is to write a "Font Server" that can
- translate text, given font and pointsize, into a blittable Sprite.
-
- The font server works nicely. :)
-
- Next challenge is how to blit the sprites. The best way is probably to
- keep an off-screen buffer and save the area behind the sprites to this
- buffer. When the sprite is removed, the screen is restored from this
- off-screen buffer. If this "back-buffer" is too big, we can possibly
- save a shred of the background in the Sprite data structure and use that
- to restore the background. The off-screen buffer is a more robust
- implementation in that it allows graphics pen styles, such as XOR, OR,
- etc.
-
- Saving the back-buffer in the sprites is a bad idea because two or
- three sprites may go over single point, and each would have a different
- idea of what the background looks like.
-
- The standard way of blitting sprites is to cycle through the sprite,
- checking against the mask, and put the pixel if it is "live". This
- requires a loop iteration for each pixel in each scanline. This can
- be fairly time consuming. A much faster method was outlined by Andrew
- Welch, and that is to compile the sprite into a continuous stream of
- pixels, arranged by a series of opcodes describing where the next pixel
- should go.
-
- By compiling the sprites, I achieved a speed up of 45%
- over the old sprite/mask method. An additional advantage is that
- the compiled sprites take much less storage space -- an average of
- 50% less space.
-
- Sound: Under certain conditions, Maelstrom wants to wait until a
- sound is completed. In the original code, Maelstrom looped, polling
- the server, waiting for the sound to finish playing. Unfortunately,
- polling the server interrupts the write() which must be restarted.
- In continuous polling, the write keeps having to be restarted and
- the sound never finishes playing. If the sound status is kept by
- the Maelstrom process, with the sound server sending periodic updates,
- then the sound server can be greatly simplified and will not be
- continuously polled. This is the new approach I will take.
-
- This approach works well enough. The sound server takes very little
- CPU time, as most of it's time is spent waiting for device writes to
- complete. One of the challenges I ran into, in writing interactive
- sound code, is you need quick response time to user events. For example,
- if the player presses the "Thrust" key, you need to be able to start the
- thrust sound, and stop it almost immediately. One of the parameters
- you can tune to get this is the fragment size of the sound-card writes.
- If you have a large fragment size, then large amounts of sound are
- quickly processed and sent to the sound device. Once it is in the
- sound device buffer, the sound cannot be cancelled. However, as the
- size of the audio-fragment decreases, you begin to have better control
- over the timing, starts and stops of the interractive sound.
-
- For Maelstrom, I found that a fragment size of 1024 (2^10)
- was about right for good response time without instantaneous response.
-
- Instantaneous response is not, in general, a good way to
- program an interactive game, such as Asteroids or Maelstrom. The
- player expects to see an organic, analog universe, not one that
- reacts instantly to conditions. In the real world, cause and effect
- relationships are not instantaneous or even immediately obvious.
- For example, when a jet passes overhead, the sound of the jet trails
- the sight of the jet by a good distance. Another example is acceleration.
- When gravity takes hold of a body, the body gradually accelerates to a
- maximum velocity -- it doesn't immediately reach its terminal velocity.
- These organic and semi-random natural conditions can be simulated in
- the computer using random vectors and calculated delays to produce an
- environment more suitable for interactive play.
-
- Timing is a crucial part of interactive games. In Maelstrom,
- large portions of code are devoted to boundary condition detection and
- timing conventions. At each frame update, all sorts of time-dependent
- conditions are checked -- how much time has it been since the last
- screen update, is it time for a keyboard check, has the shield ran out,
- etc, etc, all these are crucial components to a well-rounded game.
- If things are handled too quickly, the player no longer enjoys playing
- the game -- there is no time to repond. If things are handled too
- slowly, the game appears sluggish and is no longer fast-action fun.
-
- I'm working on clipping. Right now, clipping is handled by
- dynamically recompiling the compiled sprite when it's near the edge of
- the clipping rectangle. This is okay when there are only a few sprites
- on the screen, but when there are more than about ten or twenty, this
- becomes too slow.
- I'm going to try to add code that will detect if the sprite
- is near the edge of the clipping rectangle. If it is, it will call
- the Blit_Sprite() function, instead of the Blit_CSprite() function.
- It is faster to clip a pixmap than it is to recompile a compiled
- sprite, I think.
-
-